#pragma once

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <fstream>
#include <filesystem>
#include <string>
#include <unordered_map>
#include <atomic>
#include <windows.h>

namespace yzhzc
{
namespace log
{

// 枚举类型定义时间单位
enum class TimeUnit
{
    NOT,
    HOURLY,  // 按小时
    DAILY,   // 按天
    WEEKLY   // 按周
};


// 文件节点结构体
class LogFileNode
{
public:
    // 默认构造函数
    LogFileNode(const std::string& dir = "./log",
        TimeUnit genPolicy = TimeUnit::DAILY, int genValue = 1,
        TimeUnit purgePol = TimeUnit::DAILY, int purgeVal = 7)
        : _fileDir(normalizeDirPath(dir))
        , _generationPolicy(genPolicy)
        , _policyValue(genValue)
        , _policyDuration(calculateDuration(genPolicy, genValue))
        , _purgePolicy(purgePol)
        , _purgeValue(purgeVal)
        , _purgeDuration(calculateDuration(purgePol, purgeVal))
    {}

    ~LogFileNode()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        if (_file.is_open()) {
            _file.flush();
            _file.close();
        }
    }

    void init()
    {
        std::lock_guard<std::mutex> nodeLock(_mutex);

        if (_file.is_open()) return;

        // 确保目录存在
        std::filesystem::path dir(_fileDir);
        if (!std::filesystem::exists(dir)) {
            std::filesystem::create_directories(dir);
        }

        // 生成文件名并打开
        std::time_t timeNow = std::time(nullptr);
        std::string fileName = generateFileName(timeNow); // 生成 日期 + 小时 的文件名
        _file.open(fileName, std::ios::out | std::ios::app); // 打开文件
        if (!_file.is_open())
            throw std::runtime_error("日志文件打开失败: " + fileName);

        _generationTime = timeNow;
    }

    /**
    * @brief 写入消息到文件。
    * @param message 要写入的消息
    */
    void write(const std::string& message, std::time_t timeNow)
    {
        std::lock_guard<std::mutex> lock(_mutex);   // 加锁以确保线程安全
        checkAndRotateFile(timeNow);                // 检查是否需要切换文件
        _file << message;              // 写入消息
        if (++_writeCount >= _writeCountMAX || (timeNow - _lastWriteTime) > 60) {      // 每隔一定次数刷新缓冲区
            _file.flush();
            _writeCount = 0;
        }
        _lastWriteTime = timeNow;
    }

    /**
     * @brief 清理过期日志文件。
     * @exception std::runtime_error 如果无法获取当前时间
     */
    void purgeFiles()
    {
        using namespace std::chrono;
        const time_t nowTime = std::time(nullptr);
        if (nowTime == -1)
            throw std::runtime_error("无法获取当前时间，请检查系统时间设置。");

        for (const auto& entry : std::filesystem::directory_iterator(_fileDir)) {
            if (!entry.is_regular_file()) continue; // 只处理普通文件

            // 如果无法解析文件时间，或者无效的清理策略则跳过
            const std::time_t fileTime = getFileTime(entry.path().filename().string());
            if (fileTime <= 0 || _purgeDuration <= 0) continue;

            // 检查时间，删除过期文件
            try {
                if ((nowTime - fileTime) > _purgeDuration)
                    std::filesystem::remove(entry.path());
            }
            catch (const std::filesystem::filesystem_error& e) {
                std::cerr << "删除过期LOG文件时出错: " << entry.path() << "。错误信息: " << e.what() << std::endl;
                continue;
            }
        }
    }

private:

    // 辅助函数：确保路径以 '/' 结尾
    std::string normalizeDirPath(const std::string& dir)
    {
        if (dir.empty()) {
            return dir; // 如果路径为空，直接返回
        }

        // 使用 std::filesystem::path 处理路径
        std::filesystem::path path(dir);
        if (path.filename().empty()) {
            // 如果路径已经是目录形式（以 '/' 结尾），直接返回
            return dir;
        }
        else {
            // 否则，添加 '/' 结尾
            return (path / "").string();
        }
    }

    /**
     * @brief 检查是否需要切换日志文件，并在必要时切换。
     * @param timeNow 当前时间戳
     * @exception std::runtime_error 如果新文件打开失败
     */
    void checkAndRotateFile(std::time_t timeNow)
    {
        if ((timeNow - _generationTime) < _policyDuration) return;

        // 如果目录不存在，则创建目录
        std::filesystem::path dir(_fileDir);
        if (!std::filesystem::exists(dir))
            std::filesystem::create_directories(dir);

        std::string newFileName = generateFileName(timeNow);
        std::filesystem::path fullPath = dir / newFileName; // 构造完整路径

        // 关闭当前文件
        if (_file.is_open())
            _file.close();

        _file.open(fullPath, std::ios::out | std::ios::app); // 打开新文件
        if (!_file.is_open())
            throw std::runtime_error("无法打开文件: " + fullPath.string() + " (目录: " + _fileDir + ")");

        _generationTime = timeNow; // 更新新文件的文件生成时间
    }

    /**
     * @brief 根据时间生成文件名（跨平台版本）。
     * @param timeNow 时间戳
     * @return 生成的文件名（格式：YYYY-MM-DD_HH-MM.log）
     */
    std::string generateFileName(std::time_t time_t_now) const
    {
        struct tm tm_buf;
#if defined(_WIN32) || defined(WIN64)
        localtime_s(&tm_buf, &time_t_now); // Windows 平台
#else
        localtime_r(&time_t_now, &tm_buf); // POSIX 平台
#endif
        // 使用 strftime 格式化时间
        char buffer[32];
        // strftime(buffer, sizeof(buffer), "%Y-%m-%d_%H-%M", &tm_buf);
        strftime(buffer, sizeof(buffer), "%Y-%m-%d_%H", &tm_buf);

        return _fileDir + std::string(buffer) + ".log";
    }


    /**
 * @brief 根据时间单位和值计算时间间隔。
 * @param timeUnit 时间单位（如 HOURLY、DAILY、WEEKLY）
 * @param value 对应的时间值
 * @return 时间间隔（以秒为单位）
 */
    std::time_t calculateDuration(TimeUnit timeUnit, int value) const
    {
        switch (timeUnit) {
            case TimeUnit::HOURLY: return 60 * 60 * value;          // 每小时 = 3600 秒
            case TimeUnit::DAILY: return 24 * 60 * 60 * value;      // 每天 = 86400 秒
            case TimeUnit::WEEKLY: return 7 * 24 * 60 * 60 * value; // 每周 = 604800 秒
            default: return 0;
        }
    }

    /**
         * @brief 从文件名中提取时间戳。
         * @param filename 文件名（格式：YYYY-MM-DD_HH-MM.log）
         * @return 时间戳（std::time_t），解析失败返回 0
         */
    std::time_t getFileTime(const std::string& filename) const
    {
        // 检查文件名格式是否正确：
        // 1. 必须为 16（YYYY-MM-DD_HH-MM 的长度）
        // 2. 检查分隔符是否在正确位置：第4位是'-'，第7位是'-'，第10位是'_'，第13位是'-'
        if (filename.size() < 16 || filename[4] != '-' || filename[7] != '-' ||
            filename[10] != '_' || filename[13] != '-') {
            return 0;
        }

        // 初始化 tm 结构体，用于存储解析后的时间组件
        struct tm tm_buf = {};
        const char* ptr = filename.c_str();
        char* end;

        // 解析年份（前4个字符），减去 1900 因为 tm_year 是从 1900 年开始计数的
        tm_buf.tm_year = std::strtol(ptr, &end, 10) - 1900;
        if (end != ptr + 4) return 0;
        ptr = end + 1; // 跳过 '-'

        // 解析月份（第5-6字符），减去 1 因为 tm_mon 是 0-11 的范围
        tm_buf.tm_mon = std::strtol(ptr, &end, 10) - 1;
        if (end != ptr + 2) return 0;
        ptr = end + 1; // 跳过 '-'

        // 解析日期（第8-9字符）
        tm_buf.tm_mday = std::strtol(ptr, &end, 10);
        if (end != ptr + 2) return 0;
        ptr = end + 1; // 跳过 '_'

        // 解析小时（第11-12字符）
        tm_buf.tm_hour = std::strtol(ptr, &end, 10);
        if (end != ptr + 2) return 0;
        ptr = end + 1; // 跳过 '-'

        // 解析分钟（第14-15字符）
        tm_buf.tm_min = std::strtol(ptr, &end, 10);
        if (end != ptr + 2) return 0;

        std::time_t timeT = std::mktime(&tm_buf);
        return (timeT == -1) ? 0 : timeT;
    }

private:
    const std::string _fileDir;         // 文件目录路径

    const TimeUnit _generationPolicy;   // 文件生成策略（按小时、天、周）
    const int _policyValue;             // 生成策略值（如每几小时、几天、几周）
    const std::time_t _policyDuration;  // 生成策略的时间间隔

    const TimeUnit _purgePolicy;        // 文件清理策略
    const int _purgeValue;              // 清理策略值
    const std::time_t _purgeDuration;   // 清理策略的时间间隔

    const int _writeCountMAX = 100;

    // 运行时成员
    std::fstream _file;                 // 文件流
    std::mutex _mutex;                  // 文件锁
    int _writeCount = 0;  // 生成计数
    std::time_t _generationTime = 0;    // 文件生成时间
    std::time_t _lastWriteTime = 0;     // 最近一次写入时间
};
}
}

